﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using Microsoft.WindowsCE.Forms;
using Microsoft.WindowsMobile.PocketOutlook;
using Interfaces;

namespace Reminder
{
    public partial class ReminderControl : UserControl
    {
        private Reminder owner;
        public Reminder Owner
        {
            get { return owner; }
            set { owner = value; }
        }

        private OutlookSession outlookSession;
        private Task notificationTask;
        private TaskDialog taskDialog;
        private LocationDialog locationDialog;

        private LocationCollection locations;
        public LocationCollection Locations
        {
            get { return locations; }
        }

        private bool loadingTasks;
        private string reminderDataPath;
        private const string locationsFile = @"\locations.xml";
        private const string taskPropertyName = "ReminderLocations";
        private int eventId = -1;

        public ReminderControl(Reminder owner)
        {
            InitializeComponent();
            this.owner = owner;

            reminderDataPath = owner.Host.WorkingDirectory + @"\plugins\Reminder";
            if (!Directory.Exists(reminderDataPath))
            {
                Directory.CreateDirectory(reminderDataPath);
            }

            imageList.Images.Add(Properties.Resources.GlobeIcon);
            imageList.Images.Add(Properties.Resources.CellphoneIcon);

            outlookSession = new OutlookSession();
            //outlookSession.Appointments.Items.ListChanged += outlookSession_ListChanged;

            taskDialog = new TaskDialog(this);
            taskDialog.Closed += taskDialog_Closed;

            locationDialog = new LocationDialog(this);
            locationDialog.Closed += locationDialog_Closed;

            LoadLocations();
            LoadTasks();

            //owner.Host.ContextChanged += new Interfaces.ContextChangedHandler(Host_ContextChanged);
            SetReminder();
        }

        void taskDialog_Closed(object sender, EventArgs e)
        {
            // Moved to the end of btnTask*_Click, so it is called after the outlook dialog is closed.
            //LoadTasks();
            SetReminder();
        }

        void locationDialog_Closed(object sender, EventArgs e)
        {
            LoadLocations(); // Refresh location list.
            SaveLocations();
        }

        /*void Host_ContextChanged(object sender)
        {
            
        }*/

        private void SetReminder()
        {
            if (eventId != -1)
            {
                owner.Host.RemoveEvent(eventId);
            }

            IEnumerable<Location> activeLocations = new List<Location>();
            foreach (Task task in outlookSession.Tasks.Items)
            {
                if (task.Complete)
                {
                    continue;
                }

                IEnumerable<Location> taskLocations = GetTaskLocations(task);
                if (taskLocations != null)
                {
                    activeLocations = activeLocations.Union(taskLocations);
                }
            }

            const string description = "reminder";
            string[] ids = activeLocations
                //.Distinct()
                .SelectMany(l => l.Positions)
                .Select(p => p.Id)
                .Distinct()
                .ToArray();
            eventId = owner.Host.CreateEvent(description, 1, Host_ContextMatches, ids);
        }

        private IEnumerable<Location> GetTaskLocations(Task task)
        {
            IEnumerable<Location> q;
            if (!task.Properties.Contains(taskPropertyName)
                || task.Properties[taskPropertyName] == null
                || task.Properties[taskPropertyName].ToString() == string.Empty)
            {
                q = new List<Location>();
            }
            else
            {
                string locationString = task.Properties[taskPropertyName].ToString();
                IEnumerable<Guid> guids = locationString.Split(',').Select(s => new Guid(s));
                q = from l in locations
                    join g in guids on l.Guid equals g
                    select l;
            }

            int position = task.Subject.IndexOf('@');
            if (position > -1 && task.Subject.Length > position + 1)
            {
                string locationName = task.Subject.Substring(position + 1);
                q = q.Union(locations.Where(l => l.Name.Equals(locationName,
                    StringComparison.OrdinalIgnoreCase)));
            }

            // Get child locations.
            q = q.ToList();
            /*
            // WARNING: THIS CODE IS UNSTABLE!
            // If you figure out why, please let me know! :)
            int count = -1;
            List<Location> debug = q.ToList();
            while (q.Count() != count)
            {
                count = q.Count();
                q = q.Union(locations.Where(child => q.Contains(child.Parent)));
            }
            */
            // Replacement for the above
            AddChildLocationsToList((List<Location>)q);

            return q;
        }

        private void AddChildLocationsToList(List<Location> locationList)
        {
            bool newChildren = true;
            while (newChildren)
            {
                newChildren = false;
                foreach (Location child in locations)
                {
                    if (child.Parent == null)
                    {
                        continue;
                    }

                    if (locationList.Contains(child.Parent) &&
                        !locationList.Contains(child))
                    {
                        locationList.Add(child);
                        newChildren = true;
                    }
                }
            }
        }

        void Host_ContextMatches(object sender, int id, short relevance)
        {
            PositionOriginator[] originators = { PositionOriginator.Cell, PositionOriginator.Wifi };
            string[] positions = owner.Host.GetCurrentContext(originators);

            IEnumerable<Task> activeTasks = outlookSession.Tasks.Items.OfType<Task>()
                .Where(t => !t.Complete &&
                GetTaskLocations(t)
                .SelectMany(l => l.Positions)
                .Select(p => p.Id)
                .Intersect(positions).Count() != 0);

            ShowReminder(activeTasks.ToArray());
        }

        /*private void outlookSession_ListChanged(Object sender, ListChangedEventArgs e)
        {
            if (e.ListChangedType == ListChangedType.ItemAdded)
            {
                throw new Exception("ListChanged"); //TODO: Does this ever happen?
            }
            else if (e.ListChangedType == ListChangedType.ItemChanged)
            {
                throw new Exception("ListChanged"); //TODO: Does this ever happen?
            }
        }*/

        private void ShowReminder(Task[] tasks)
        {
            if (tasks == null || tasks.Length == 0)
            {
                return;
            }
            notificationTask = tasks[0];

            StringBuilder htmlString = new StringBuilder();
            htmlString.Append("<html><body>");
            htmlString.Append("<b>");
            //htmlString.Append(string.Join(", ", tasks.Select(t => t.Subject).ToArray()));
            htmlString.Append(tasks[0].Subject);
            htmlString.Append("</b><br>");
            if (tasks.Length > 1)
            {
                htmlString.Append(tasks.Length - 1);
                htmlString.Append(" other task(s) to be done here.<br>");
            }
            htmlString.Append("<form method='GET' action='formsubmit'>");
            htmlString.Append("<input type='checkbox' name='complete' id='complete'>");
            htmlString.Append("<label for='complete'>Mark the task complete</label><br>");
            htmlString.Append("<input type='button' name='cmd:2' value='Hide'>");
            htmlString.Append("<input type='submit' name='dismiss' value='Dismiss'>");
            htmlString.Append("<input type='submit' name='show' value='Show task'>");
            htmlString.Append("</form>");
            htmlString.Append("</body></html>");

            notification.Text = htmlString.ToString();

            //notification.Icon = Properties.Resources.NoficitaionIcon;
            notification.Caption = "Reminder";
            notification.Critical = false;
            notification.Visible = true;
        }

        /*private void button1_Click(object sender, EventArgs e)
        {
            string categories = string.Empty;
            string subject = string.Empty;
            foreach (var item in outlookSession.Appointments.Items)
            {
                categories = item.Categories;
                subject = item.Subject;
                notificationTask = item;
            }
            StringBuilder htmlString = new StringBuilder();
            htmlString.Append("<html><body>");
            htmlString.Append("<b>");
            htmlString.Append(subject);
            htmlString.Append("</b><br>");
            htmlString.Append(categories);
            //htmlString.Append("<form method='GET' action='notify'>");
            //htmlString.Append("<input type='submit'>");
            //htmlString.Append("<a href='appointment'>Link</a>");
            htmlString.Append("<br>");
            htmlString.Append("<input type='button' name='show' value='Show task'>");
            htmlString.Append("<input type='button' name='cmd:2' value='Hide'>");
            htmlString.Append("<input type='button' name='dismiss' value='Dismiss'>");
            //htmlString.Append("</form>");
            htmlString.Append("</body></html>");

            // Set the Text property to the HTML string.
            notification.Text = htmlString.ToString();

            //notification.Icon = Properties.Resources.NoficitaionIcon;
            notification.Caption = "Reminder";
            notification.Critical = false;

            // Display icon up to 10 seconds.
            //notification.InitialDuration = 10;
            notification.Visible = true;
        }*/

        private void notification_ResponseSubmitted(object sender, ResponseSubmittedEventArgs e)
        {
            // "formsubmit?complete=on&show=Show+task"
            if (e.Response.Substring(0, 10) == "formsubmit")
            {
                if (e.Response.Contains("complete=on"))
                {
                    notificationTask.Complete = true;
                    notificationTask.Update();
                }

                if (e.Response.Contains("show="))
                {
                    if (notificationTask != null)
                    {
                        notificationTask.ShowDialog();
                    }
                }
                else if (e.Response.Contains("dismiss="))
                {
                }
            }

            notification.Visible = false;
        }

        private void LoadLocations()
        {
            if (locations == null)
            {
                string file = reminderDataPath + locationsFile;
                try
                {
                    locations = LocationCollection.Load(file);
                }
                catch (FileNotFoundException)
                {
                    locations = new LocationCollection();
                    SaveLocations();
                }
            }

            // Update lvwLocations.
            lvwLocations.Items.Clear();
            foreach (Location location in locations)
            {
                ListViewItem item = new ListViewItem(location.Name);
                item.Tag = location;
                item.ImageIndex = (int)location.Type;
                lvwLocations.Items.Add(item);
            }
        }

        private void SaveLocations()
        {
            string file = reminderDataPath + locationsFile;
            locations.Save(file);
        }

        private void LoadTasks()
        {
            lvwTasks.Items.Clear();
            loadingTasks = true;
            foreach (Task task in outlookSession.Tasks.Items)
            {
                ListViewItem item = new ListViewItem(task.Subject);
                item.Checked = task.Complete;
                item.Tag = task;
                lvwTasks.Items.Add(item);
            }
            loadingTasks = false;
        }

        private void lvwTasks_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (!loadingTasks)
            {
                Task task = (Task)lvwTasks.Items[e.Index].Tag;
                task.Complete = (e.NewValue == CheckState.Checked);
                task.Update();
            }
        }

        private void lvwTasks_SelectedIndexChanged(object sender, EventArgs e)
        {
            bool isSelected = (lvwTasks.SelectedIndices.Count > 0);
            btnTaskEdit.Enabled = isSelected;
            btnTaskDelete.Enabled = isSelected;
        }

        private void btnTaskNew_Click(object sender, EventArgs e)
        {
            Task task = new Task();
            outlookSession.Tasks.Items.Add(task);
            taskDialog.Task = task;
            taskDialog.ShowDialog();
            LoadTasks();
        }

        private void btnTaskEdit_Click(object sender, EventArgs e)
        {
            Task task = (Task)lvwTasks.Items[lvwTasks.SelectedIndices[0]].Tag;
            taskDialog.Task = task;
            taskDialog.ShowDialog();
            LoadTasks();
        }

        private void btnTaskDelete_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show(
                "The selected task will be permanently deleted. Continue?",
                "Reminder",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Exclamation,
                MessageBoxDefaultButton.Button2);
            if (result != DialogResult.Yes)
            {
                return;
            }

            Task task = (Task)lvwTasks.Items[lvwTasks.SelectedIndices[0]].Tag;
            task.Delete();
            LoadTasks();
        }

        private void lvwLocations_SelectedIndexChanged(object sender, EventArgs e)
        {
            bool isSelected = (lvwLocations.SelectedIndices.Count > 0);
            btnLocationEdit.Enabled = isSelected;
            btnLocationDelete.Enabled = isSelected;
        }

        private void btnLocationNew_Click(object sender, EventArgs e)
        {
            Location location = new Location();
            locations.Add(location);
            locationDialog.SelectedLocation = location;
            locationDialog.ShowDialog();
        }

        private void btnLocationEdit_Click(object sender, EventArgs e)
        {
            Location location = (Location)lvwLocations.Items[lvwLocations.SelectedIndices[0]].Tag;
            locationDialog.SelectedLocation = location;
            locationDialog.ShowDialog();
        }

        private void btnLocationDelete_Click(object sender, EventArgs e)
        {
            DialogResult result = MessageBox.Show(
                "The selected location will be permanently deleted. Continue?",
                "Reminder",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Exclamation,
                MessageBoxDefaultButton.Button2);
            if (result != DialogResult.Yes)
            {
                return;
            }

            Location location = (Location)lvwLocations.Items[lvwLocations.SelectedIndices[0]].Tag;
            // Delete location, remove as parent.
            if (locations.Remove(location))
            {
                foreach (Location loc in locations)
                {
                    if (loc.Parent == location)
                    {
                        loc.Parent = null;
                    }
                }
                SaveLocations();
            }
            else
            {
                // This should never happen.
                MessageBox.Show(
                    "Error: Cannot delete location!",
                    "Reminder",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button1);
            }
            LoadLocations();
        }
    }
}
